﻿using System.Globalization;
using System.Linq;
using BlogsAPI.Interfaces;
using Facebook;
using Microsoft.CSharp.RuntimeBinder;
using RssToolkit.Rss;

namespace BlogsAPI.Sites
{
    using System.Collections.Generic;
    using Shorteners;
    using Newtonsoft.Json.Linq;
    using System.Web;
    using System;
    using System.Diagnostics;
    using System.IO;
    using System.Net;
    using System.Text;
    using System.Text.RegularExpressions;
    using Abstract;

    /// <summary>
    /// Статистика страницы
    /// </summary>
    public struct FaceStats
    {
        /// <summary>
        /// Идентификатор
        /// </summary>
        public string ID;
        /// <summary>
        /// Псевдоним страницы, если есть
        /// </summary>
        public string Username;
        /// <summary>
        /// Число лайков на странице
        /// </summary>
        public long Likes;
        /// <summary>
        /// Уровень активности
        /// </summary>
        public long Talks;
        /// <summary>
        /// Имя страницы
        /// </summary>
        public string Name;
        /// <summary>
        /// Описание страницы
        /// </summary>
        public string About;
        /// <summary>
        /// Подпись под именем
        /// </summary>
        public string Subtitle;
        /// <summary>
        /// Ошибка ключа доступа
        /// </summary>
        public bool IsAuthError;
        /// <summary>
        /// An unexpected error has occurred. Please retry your request later.
        /// </summary>
        public bool IsServerError;
        /// <summary>
        /// Имеет ли галочку?
        /// </summary>
        public bool IsVerified;
        /// <summary>
        /// Последние посты 
        /// </summary>
        public List<BlogPost> Feed;
    }

    /// <summary>
    /// Класс для взаимодействия с крупнейшей социальной сетью Facebook путем доступа через прямое залогинивание!
    /// </summary>
    /// <summary>Для дополнительной информации используем facebook-csharp-sdk</summary>
    /// <website>https://github.com/facebook-csharp-sdk</website>
    /// <website>http://csharpsdk.org/</website>
    /// <website>http://blog.prabir.me/post/Facebook-CSharp-SDK-Making-Requests.aspx</website>
    [BlogServiceAPI(Description = "Facebook", Link = "http://facebook.com")]
    public class Facebook : BlogServer, ILike
    {
        #region Properties 

        /// <summary>
        /// Ключ доступа для запросов
        /// </summary>
        private string AccessToken;

        /// <summary>
        /// Экземпляр класса для запросов к Фейсбуку
        /// </summary>
        private FacebookClient FbClient
        {
            get
            {
                if (_FbClient != null) return _FbClient;
                _FbClient = String.IsNullOrEmpty(AccessToken) ? new FacebookClient() : new FacebookClient(AccessToken);
                return _FbClient;
            }
        }

        private FacebookClient _FbClient;

        /// <summary>
        /// Параметры запроса постов
        /// </summary>
        private static readonly string FeedParams =
            "feed{created_time,from,description,story,place,shares,picture,type,link,message,story_tags,message_tags,comments{like_count},reactions.limit(0).summary(1)}";

        private static readonly Regex Validation = new Regex(@"^https?://(?:[-_\w]+\.)?facebook\.com", Regexoptions);
        private static readonly Regex ValidationFull = new 
            Regex(@"^https?://(?:[-_\w]+\.)?facebook\.com(?:/app_scoped_user_id)?/([0-9a-z\._-]+(?:/|$)|[0-9]+(?=_[0-9]+)|.*?id\=(\d+)(?:\D|$))", Regexoptions);
        
        /// <summary>
        /// Определение, что пост это пост
        /// </summary>
        private static readonly Regex rObjectID = new Regex(@"\b(\d+)_(\d+)\b", RegexOptions.Compiled | RegexOptions.Singleline);
        private static readonly Regex rPostID = new Regex(@"(?:/posts/|fbid=|/photos/[^/]+/)(\d+)\b", RegexOptions.Compiled | RegexOptions.Singleline);
        private static readonly Regex rID = new Regex(@"\D(\d+)$", Regexoptions);
        private static readonly Regex rID2 = new Regex(@"(?:^|[^\w\.])(\d+)(?:$|\W)", Regexoptions);

        #endregion

        #region interface: BlogServer

        [DebuggerStepThrough]
        public Facebook(): this(null)
        {
        }

        /// <summary>
        /// Ключ для запоросов
        /// </summary>
        /// <param name="accessToken"></param>
        [DebuggerStepThrough]
        public Facebook(string accessToken)
        {
            Encoding = Encoding.UTF8;
            AccessToken = accessToken;
            
        }

        /// <summary>
        /// http://www.t30p.ru/FaceBook.aspx
        /// </summary>
        public override bool HasRating
        {
            get { return true; }
        }

        /// <summary>
        /// проверка, является ли передаваемый урл чатью этого сервиса
        /// </summary>
        public override Boolean ValidateLink(String sLink)
        {
            return Validation.Match(sLink).Success;
        }

        /// <summary>
        /// проверка, является ли передаваемый урл чатью этого сервиса
        /// </summary>
        /// <param name="sLink">ссылка для проверки</param>
        /// <param name="userName">Имя пользователя на блогохостинге</param>
        /// <param name="bType">Тип блога - пользователь или сообщество</param>
        public override Boolean ValidateLink(String sLink, out String userName,out BlogType bType)
        {
            if (sLink.Contains("?") && !sLink.Contains(".php?"))//лучший гавнокод!
            {
                sLink = sLink.Substring(0, sLink.IndexOf('?'));
            }
            bType = BlogType.Unknown;
            Match m = ValidationFull.Match(sLink);
            if (m.Success)
            {
                //http://www.facebook.com/tvrain 
                //http://facebook.com/app_scoped_user_id/100001324039545/
                //http://www.facebook.com/profile.php?id=100000869227964
                //https://www.facebook.com/photo.php?fbid=410520499050191&set=a.220786401356936.34773.210681349034108&type=1
                //http://facebook.com/125909647492772_502974003098530 - if its a post
                bType = BlogType.User;
                userName = m.Groups[2].Success ? m.Groups[2].Value : m.Groups[1].Value.TrimEnd('/').ToLower();
                //https://www.facebook.com/people/%EC%84%9C%EC%98%88%EC%A7%80/100011056275300
                if(String.Compare(userName, "people",true)==0){
                    var id = rID.Match(sLink);
                    if (id.Success)
                    {
                        userName = id.Groups[1].Value;
                    }
                }
            }
            else
            {
                userName = String.Empty;
            }
            return m.Success;
        }

        /// <summary>
        /// Получение ссылки на блог блогера
        /// </summary>
        /// <param name="userName">ник блогера</param>
        /// <param name="btype">Тип блога - пользователь или сообщество</param>
        public override String GetBlogLink(string userName, BlogType btype)
        {
            return String.Format("http://www.facebook.com/{0}", userName.ToLower());
        }

#endregion

        HttpWebRequest myHttpWebRequest;
        HttpWebResponse myHttpWebResponse;

        /// <summary>
        /// Специальный код достижения предела по запросам
        /// </summary>
        public static int LimitErrorCode = -4;

        /// <summary>
        /// ключ необходимый для любой формы
        /// </summary>
        private readonly Regex rPostFormId = new Regex(@"name\W+post_form_id\W+value\W+(\w+)\W", Regexoptions);

        /// <summary>
        /// ключ необходимый для любой формы
        /// </summary>
        private readonly Regex rComposerId = new Regex(@"name\W+xhpc_composerid\W+value\W+([^'""]+)\W", Regexoptions);
        
        /// <summary>
        /// ключ необходимый для любой формы
        /// </summary>
        private readonly Regex rdtsg= new Regex(@"name\W+fb_dtsg\W+value\W+([^'""]+)\W", Regexoptions);

        /// <summary>
        /// ключ необходимый для любой формы
        /// </summary>
        /// private readonly Regex rUrl = new Regex(@"name\W+url\W+value\W+([^'""]+)\W", Regexoptions);

        /// <summary>
        /// Разбор рекламных страниц
        /// </summary>
        private readonly Regex rParser = new Regex(@"href\W+http\W+(?:www.)?facebook.com\W+([\w\._-]+)(?:[^""']+?(\d{10,}))?[^""']+\S+\sonclick", Regexoptions);

        /// <summary>
        /// Обновление определенного потока по номеру
        /// </summary>
        /// <param name="user"></param>
        /// <param name="rssid"></param>
        /// <returns></returns>
        [Obsolete("Use Facebook SDK")]
        public Boolean UpdateRss(UserAccount user, string rssid)
        {
            if (LogInto(user))
            {
                try
                {
                    myHttpWebRequest =
                        MakeRequest("http://www.facebook.com/?ref=home");
                    myHttpWebRequest.Method = "GET"; // метод GET
                    myHttpWebRequest.Referer = "http://facebook.com";
                    myHttpWebRequest.AllowAutoRedirect = false;
                    //получаем ответ и его разбираем...
                    myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
                    string result = new StreamReader(myHttpWebResponse.GetResponseStream(), Encoding.UTF8).ReadToEnd();
                    myHttpWebResponse.Close();
                    //
                    string post_form_id = rPostFormId.Match(result).Groups[1].Value;
                    ////////////////////////
                    ///запрос на обновление
                    /// ////////////////////
                    int i = 1;
                    do
                    {
                        myHttpWebRequest = MakeRequest("http://www.facebook.com/ajax/feedcrawl.php?__a=1");
                        myHttpWebRequest.Method = "POST"; // метод GET
                        myHttpWebRequest.Referer = "http://facebook.com";
                        myHttpWebRequest.AllowAutoRedirect = false;

                        myHttpWebRequest.ContentType = "application/x-www-form-urlencoded";
                        // передаем параметры
                        //id="post_form_id" name="post_form_id" value="0ae76ef7e6077035e571580cefa9fc8d"
                        string sQueryString =
                            String.Format(
                                "id={0}&action=update_story&post_form_id={1}&fb_dtsg=I624T&post_form_id_source=AsyncRequest",
                                rssid, post_form_id
                                );
                        byte[] byteArr = Encoding.UTF8.GetBytes(sQueryString);
                        myHttpWebRequest.ContentLength = byteArr.Length;
                        myHttpWebRequest.GetRequestStream().Write(byteArr, 0, byteArr.Length);

                        //id=17404430442&action=update_story&post_form_id=0ae76ef7e6077035e571580cefa9fc8d&fb_dtsg=I624T&post_form_id_source=AsyncRequest
                        myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
                        result = new StreamReader(myHttpWebResponse.GetResponseStream(), Encoding.UTF8).ReadToEnd();
                        myHttpWebResponse.Close();
                        if (i++ > 10) return false;
                    } while (result.Contains("url you entered is not valid"));
                    return true;
                }
                catch (Exception e1)
                {
                    Debug.WriteLine(e1.Message);
                }
            }
            return false;
        }
        /// <summary>
        /// Ручное залогинивание
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public override Boolean LogInto(UserAccount user)
        {
            try
            {
                myHttpWebRequest = MakeRequest("https://login.facebook.com/");
                myHttpWebRequest.Method = "GET"; // метод GET
                myHttpWebRequest.Referer = "http://facebook.com";
                myHttpWebRequest.AllowAutoRedirect = false;
                //получаем ответ и его разбираем...
                myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
                sCookie = CookieParse(myHttpWebResponse.Headers[HttpResponseHeader.SetCookie]);
                sCookie += "; reg_fb_ref=http%3A%2F%2Fwww.facebook.com%2F";
                //string result = new StreamReader(myHttpWebResponse.GetResponseStream(), Encoding.UTF8).ReadToEnd();
                myHttpWebResponse.Close();

                ///а теперь сам запрос
                myHttpWebRequest = MakeRequest("https://login.facebook.com/login.php?login_attempt=1");
                myHttpWebRequest.Method = "POST"; // метод GET
                myHttpWebRequest.Referer = "http://facebook.com";
                myHttpWebRequest.AllowAutoRedirect = false;
                //charset_test=%E2%82%AC%2C%C2%B4%2C%E2%82%AC%2C%C2%B4%2C%E6%B0%B4%2C%D0%94%2C%D0%84&locale=en_US&email=onestory%40yandex.ru&pass=qQwklopq&charset_test=%E2%82%AC%2C%C2%B4%2C%E2%82%AC%2C%C2%B4%2C%E6%B0%B4%2C%D0%94%2C%D0%84&lsd=NT7ew
                myHttpWebRequest.ContentType = "application/x-www-form-urlencoded";
                // передаем параметры
                string sQueryString =
                    String.Format(
                        "charset_test=%E2%82%AC%2C%C2%B4%2C%E2%82%AC%2C%C2%B4%2C%E6%B0%B4%2C%D0%94%2C%D0%84&locale=en_US&email={0}&pass={1}&charset_test=%E2%82%AC%2C%C2%B4%2C%E2%82%AC%2C%C2%B4%2C%E6%B0%B4%2C%D0%94%2C%D0%84&lsd=NT7ew",
                        HttpUtility.UrlEncode(user.username),
                        HttpUtility.UrlEncode(user.password)
                        );
                byte[] byteArr = Encoding.UTF8.GetBytes(sQueryString);
                myHttpWebRequest.ContentLength = byteArr.Length;
                myHttpWebRequest.GetRequestStream().Write(byteArr, 0, byteArr.Length);

                string result;
                //получаем ответ и его разбираем...
                myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
                if(myHttpWebResponse.Headers["Location"].Contains("setup_machine"))//то нужно придумать имя для машины =(.
                {
                    sCookie = CookieParse(myHttpWebResponse.Headers[HttpResponseHeader.SetCookie]);
                    myHttpWebRequest = MakeRequest("https://login.facebook.com/loginnotify/setup_machine.php");
                    myHttpWebRequest.Method = "GET"; // метод GET
                    myHttpWebRequest.AllowAutoRedirect = false;
                    //получаем ответ и его разбираем...
                    myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
                    result = new StreamReader(myHttpWebResponse.GetResponseStream(), Encoding.UTF8).ReadToEnd();
                    string post_form_id = rPostFormId.Match(result).Groups[1].Value;
                    //post_form_id =( and lsd =(
                    myHttpWebRequest = MakeRequest("https://login.facebook.com/loginnotify/setup_machine.php");
                    myHttpWebRequest.Method = "POST"; // метод GET
                    myHttpWebRequest.Referer = "http://facebook.com";
                    myHttpWebRequest.AllowAutoRedirect = false;
                    myHttpWebRequest.ContentType = "application/x-www-form-urlencoded";
                    string MNAME = "machinename=BlogsAPI&remembercomputer=1&post_form_id=" + post_form_id;
                    byteArr = Encoding.UTF8.GetBytes(MNAME);
                    myHttpWebRequest.ContentLength = byteArr.Length;
                    myHttpWebRequest.GetRequestStream().Write(byteArr, 0, byteArr.Length);

                    //получаем ответ и его разбираем...
                    myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
                    //throw new Exception("Need to set a machine name or better turen it off");
                }
#if DEBUG
                result = new StreamReader(myHttpWebResponse.GetResponseStream(), Encoding.UTF8).ReadToEnd();
#endif
                sCookie = CookieParse(myHttpWebResponse.Headers[HttpResponseHeader.SetCookie]);
                //

                myHttpWebResponse.Close();
                if (sCookie.Contains("c_user"))
                {
                    sCookie = new Regex(@"\s[\w_]+\Wdeleted\W", Regexoptions).Replace(sCookie,"");
                    LoggedInAs = user;
                }
                return sCookie.Contains("c_user");
            }
            catch(Exception e1)
            {
                Debug.WriteLine("Facebook: "+e1.Message);
                return false;
            }
        }

        /// <summary>
        /// Помещение информации на страницу пользователя
        /// </summary>
        /// <param name="user">Аккаунт пользователя</param>
        /// <param name="linktopage">Ссылка на страницу страницы</param>
        /// <returns>Текст ошибки</returns>
        [Obsolete("Use Facebook SDK")]
        public String PostToMyPage(UserAccount user, string linktopage,BlogPost bp)
        {
            if (LogInto(user))
            {
                try
                {
                    string walllink = "?sk=wall";
                    string pageid = Regex.Match(linktopage,@"\D(\d+)$", Regexoptions).Groups[1].Value;
                    myHttpWebRequest = MakeRequest(linktopage + walllink);
                    myHttpWebRequest.Method = "GET"; // метод GET
                    myHttpWebRequest.AllowAutoRedirect = false;
                    myHttpWebRequest.Referer = "http://www.facebook.com";
                    //получаем ответ и его разбираем...
                    myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();

                    if (!String.IsNullOrEmpty(myHttpWebResponse.Headers["Location"]))
                    {
                        myHttpWebRequest = MakeRequest(myHttpWebResponse.Headers["Location"] + walllink);
                        myHttpWebRequest.Method = "GET"; // метод GET
                        myHttpWebRequest.Referer = "http://facebook.com";
                        //получаем ответ и его разбираем...
                        myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
                    }

                    string result = new StreamReader(myHttpWebResponse.GetResponseStream(), Encoding.UTF8).ReadToEnd();
                    myHttpWebResponse.Close();
                    //
                    string post_form_id = rPostFormId.Match(result).Groups[1].Value;
                    string coposer_id = rComposerId.Match(result).Groups[1].Value;
                    //string url = rUrl.Match(result).Groups[1].Value;
                    string fb_dtsg = rdtsg.Match(result).Groups[1].Value;
                     //post_form_id =( and lsd =(
                    myHttpWebRequest = MakeRequest("http://www.facebook.com/ajax/profile/composer.php");
                    myHttpWebRequest.Method = "POST"; // метод GET
                    myHttpWebRequest.Referer = "http://facebook.com";
                    myHttpWebRequest.AllowAutoRedirect = false;
                    myHttpWebRequest.ContentType = "application/x-www-form-urlencoded";

                    if (bp.Link!=null &&
                       bp.Link.OriginalString.ToLower().StartsWith("http://bit.ly"))
                    {
                        BitLy  oServer = new BitLy();
                        bp.Link = new Uri(oServer.ConvertDataTo(bp.Link.OriginalString, ItemType.ShortUrl, ItemType.FullUrl));
                    }
                    /*
                    string QueryString = String.Format(
                        "app_id=2309869772&post_form_id={0}&fb_dtsg={1}&xhpc_composerid={2}&xhpc_targetid={3}&xhpc_context=profile&xhpc_fbx=&aktion=post&UIThumbPager_Input=0&" +
                        "attachment[params][metaTagMap][0][http-equiv]=content-type&attachment[params][metaTagMap][0][content]=text%2Fhtml%3B%20charset%3Dutf-8&" +
                        "attachment[params][metaTagMap][1][http-equiv]=Content-Type&attachment[params][metaTagMap][1][content]=text%2Fhtml%3B%20charset%3Dutf-8&attachment[params][medium]=106&" +
                        "attachment[params][urlInfo][user]={6}&attachment[params][favicon]=http%3A%2F%2Ft30p.ru%2Ffavicon.gif&attachment[params][title]={7}&" +
                        "attachment[params][external_author]=&attachment[params][summary]={8}&" +
                        "attachment[params][url]={6}&attachment[params][ttl]=0&attachment[params][error]=1&" +
                        "attachment[params][responseCode]=200&attachment[params][links][0][rel]=shortcut%20icon&attachment[params][images][0]={9}&" +//&attachment[params][links][0][type]=image%2Fgif
                        "attachment[type]=100&post_form_id_source=AsyncRequest"
                        //composer_id=c4cdf0231b8b519868181958&hey_kid_im_a_composer=true&display_context=profile&
                    , post_form_id, fb_dtsg, coposer_id, pageid, 0, 0,
                    bp.Link == null ? "" : HttpUtility.UrlEncode(bp.Link.OriginalString),
                    bp.SubjectUrlEnc,
                    bp.BodyUrlEnc,
                    String.IsNullOrEmpty(bp.SpecialParam) ? "" : HttpUtility.UrlEncode(bp.SpecialParam));//NOTE: BIT.LY LINK IS NOT ALLOWED!
                     */

                    string QueryString =
                        String.Format(
                            "__a=1&scheduled=0&fb_dtsg={1}&xhpc_composerid={2}&xhpc_targetid={3}&xhpc_context=profile&xhpc_fbx=1&xhpc_timeline=&xhpc_ismeta=&aktion=post" +
                            "&app_id=2309869772&UIThumbPager_Input=0"+
                            "attachment[params][metaTagMap][0][http-equiv]=content-type&attachment[params][metaTagMap][0][content]=text%2Fhtml%3B%20charset%3Dutf-8&"+
                            "attachment[params][metaTagMap][1][http-equiv]=X-UA-Compatible&attachment[params][metaTagMap][1][content]=IE%3DEmulateIE7%3B%20IE%3DEmulateIE9&"+
                            "attachment[params][metaTagMap][2][http-equiv]=Content-Type&attachment[params][metaTagMap][2][content]=text%2Fhtml%3B%20charset%3Dutf-8&"+
                            "attachment[params][metaTagMap][3][http-equiv]=Content-Type&attachment[params][metaTagMap][3][content]=text%2Fhtml%3B%20charset%3Dutf-8&"+
                            "attachment[params][medium]=106&attachment[params][urlInfo][user]={4}&attachment[params][favicon]=http%3A%2F%2Ft30p.ru%2Ffavicon.gif"+
                            "&attachment[params][title]={5}"+
                            "&attachment[params][fragment_title]=&attachment[params][external_author]=&attachment[params][summary]={6}&attachment[params][url]={4}&attachment[params][ttl]=604800&attachment[params][error]=1&attachment[params][responseCode]=200&"+
                            "attachment[params][images][0]={7}&attachment[params][from_cache]=1&attachment[params][cache_hit]=1&attachment[type]=100&lsd&post_form_id_source=AsyncRequest&xhpc_message_text={4}&xhpc_message={4}&url={8}"
                            , post_form_id, fb_dtsg, coposer_id, pageid,
                    bp.Link == null ? "" : HttpUtility.UrlEncode(bp.Link.OriginalString),
                    bp.SubjectUrlEnc,
                    bp.BodyUrlEnc,
                    String.IsNullOrEmpty(bp.SpecialParam) ? "" : HttpUtility.UrlEncode(bp.SpecialParam),
                    HttpUtility.UrlEncode(linktopage));//NOTE: BIT.LY LINK IS NOT ALLOWED!
                     
                    byte[] byteArr = Encoding.UTF8.GetBytes(QueryString);
                    myHttpWebRequest.ContentLength = byteArr.Length;
                    myHttpWebRequest.GetRequestStream().Write(byteArr, 0, byteArr.Length);

                    //получаем ответ и его разбираем...
                    myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
                    //throw new Exception("Need to set a machine name or better turen it off");
                    result = new StreamReader(myHttpWebResponse.GetResponseStream(), Encoding.UTF8).ReadToEnd();
                    //
                    myHttpWebResponse.Close();
                    if (result.Contains("errorDescription\":\""))
                    {
                        Debug.WriteLine(result);
                        return result;
                    }
                }
                catch (Exception e1)
                {
                    Debug.WriteLine(e1.Message);
                    return e1.Message + " at " + linktopage + " for " + bp.Link;
                }
            }
            return String.Empty;
        }

        /// <summary>
        /// Извлечение информации о пользователе фейсбука по входным данным
        /// </summary>
        /// <param name="app_id">код приложения</param>
        /// <param name="app_secret">сексетный ключ</param>
        /// <param name="ret_url">путь обратного вызова</param>
        /// <param name="code">код возвращения</param>
        /// <returns>Информацию о пользователе</returns>
        /// <remarks>http://forum.developers.facebook.net/viewtopic.php?pid=255231</remarks>
        public JObject GetUserByCode(string app_id,string app_secret, string ret_url,string code)
        {
            string token_url =
                String.Format(//&redirect_uri={1}
                    "https://graph.facebook.com/oauth/access_token?client_id={0}&redirect_uri={1}&client_secret={2}&code={3}",
                    app_id,
                    HttpUtility.UrlEncode(ret_url),
                    app_secret,//secret
                    code
                    );
            HttpWebRequest myHttpWebRequest = (HttpWebRequest)HttpWebRequest.Create(token_url);
            string access_token = new StreamReader(myHttpWebRequest.GetResponse().GetResponseStream()).ReadToEnd();
            //получили токен, теперь запросим пользвоателя
            myHttpWebRequest = (HttpWebRequest)HttpWebRequest.Create("https://graph.facebook.com/me?" + access_token);
            string json_user = new StreamReader(myHttpWebRequest.GetResponse().GetResponseStream()).ReadToEnd();
            return JObject.Parse(json_user); ;//http://www.facebook.com/profile.php?id=100000275113518
        }

        #region interface IData

        /// <summary>
        /// Получение числа комментов к записи
        /// </summary>
        /// <param name="bp"></param>
        /// <returns></returns>
        public override int HowManyComments(BlogPost bp)
        {
            return HowManyComments(bp, "");
        }

        /// <summary>
        /// Getting comments count
        /// </summary>
        /// <param name="bp"></param>
        /// <returns></returns>
        public int HowManyComments(BlogPost bp, String postID = "")
        {
            int iRet = 0;
            try
            {
                if (String.IsNullOrEmpty(postID))
                {
                    postID = TryGetPostIdAdvanced(bp.Link.OriginalString);
                }
                if (!String.IsNullOrEmpty(postID))
                    {
                        dynamic data = FbClient.Get(postID + "/comments?summary=1&limit=1"); //
                        if (data != null &&
                            data.summary != null &&
                            data["summary"].total_count != null)
                        {
                            return int.Parse(data["summary"]["total_count"].ToString());
                            //Только так, ибо вернули нам long
                        }
                    }
                return GetManyPost(bp.Link.OriginalString)[0].Comments;
            }
            catch (FacebookOAuthException oauth)
            {
                iRet = -oauth.ErrorCode;//дурацкая ошибка от Facebook via SO
                //4 == request limit
            }
            catch (Exception e1)
            {
                Debug.Write(e1);
            }
            return iRet;
        }

        /// <summary>
        /// Getting likes count
        /// </summary>
        /// <param name="bp"></param>
        /// <returns></returns>
        public int HowManyLikes(BlogPost bp, string postID = "")
        {
            int iRet = 0;
            try
            {
                if (String.IsNullOrEmpty(postID))
                {
                    postID = TryGetPostIdAdvanced(bp.Link.OriginalString);
                }
                if (!String.IsNullOrEmpty(postID))
                {
                    dynamic data = FbClient.Get(postID + "/likes?summary=1&limit=1"); //
                    if (data != null &&
                        data.summary != null &&
                        data["summary"].total_count != null)
                    {
                        return int.Parse(data["summary"]["total_count"].ToString());
                        //Только так, ибо вернули нам long
                    }
                }
                return GetManyPost(bp.Link.OriginalString)[0].Views;
                //лайки как просмотры
            }
            catch (FacebookOAuthException oauth)
            {
                iRet = -oauth.ErrorCode;
                Debug.Write(oauth);
            }
            catch (Exception e1)
            {
                Debug.Write(e1);
            }
            return iRet;
        }

        /// <summary>
        /// Попытка получить идентификатор поста, его номер
        /// </summary>
        /// <param name="link"></param>
        /// <returns></returns>
        public string TryGetPostId(string link)
        {
            string postID = String.Empty;
            try
            {
                Match m = ValidationFull.Match(link);
                //http://facebook.com/125909647492772_502974003098530 - if its a post
                //http://facebook.com/125909647492772/posts/502974003098530 - if its a post
                //https://www.facebook.com/Spicyblogger/photos/a.1397040033851838.1073741828.1397038217185353/1627611170794722/?type=3
                var rObj = rObjectID.Match(m.Groups[1].Success ? m.Groups[1].Value.ToLower() : m.Groups[2].Value);
                if (rObj.Success)
                {
                    postID = rObj.Groups[2].Value; //postID.Split('_')[1];
                }
                else
                {
                    rObj = rPostID.Match(link);
                    if (rObj.Success)
                    {
                        postID = rObj.Groups[1].Value;
                    }
                }
            }
            catch (Exception e1)
            {
                
            }
            return postID;
        }

        /// <summary>
        /// Попытка получить идентификатор поста, его номер
        /// </summary>
        /// <param name="link"></param>
        /// <returns></returns>

        public string TryGetPostIdAdvanced(string link)
        {
            ulong id;
            //!post - -https://www.facebook.com/ScorpioSeason/posts/435074076542009
            //+https://www.facebook.com/155594511156635/posts/435074076542009
            //!photo - https://www.facebook.com/photo.php?fbid=197148637086766&set=a.159797157488581.35584.159795320822098&type=1
            //!video - http://www.facebook.com/video/video.php?v=425780967470067
            //(12) notes API is deprecated for versions v2.0 and higher (12)
            //-note - http://www.facebook.com/notes/french-lick-casino/slot-machines-and-table-games-progressive-jackpot-amounts-october-14/188477597831500
            //!event - https://www.facebook.com/events/949495705145130/
            //-story - https://www.facebook.com/permalink.php?id=308330266014520&story_fbid=468234860024059
            //http://www.facebook.com/1487099701564032/photos/a.1488101461463856.1073741833.1487099701564032/1640146829592651/?type=3 ?
            var postId = TryGetPostId(link);
            if (!String.IsNullOrEmpty(postId))
            {
                //(#12) singular links API is deprecated for versions v2.4 and higher",
                //var matchUserId = rID2.Match(link.Split(new[] { "/posts/" }, StringSplitOptions.RemoveEmptyEntries)[0]);
                //  dynamic data = link.Contains("/posts/") && matchUserId.Success
                //    ? FbClient.Get(matchUserId.Groups[1].Value + "_" + postId)
                //  : //userid_postid
                return postId;
            }
            //https://www.facebook.com/kulturologia/videos/880231712025443/
            //https://www.facebook.com/events/949495705145130/
            else if (link.Contains("/video") || link.Contains("/videos/") || link.Contains("/photos/") || link.Contains("/events/"))
            {
                if (ulong.TryParse(HttpUtility.ParseQueryString(new Uri(link).Query)["v"], out id)
                    || ulong.TryParse(rID2.Match(link.Split(new[] { "/videos/", "/video", "/events/", "/photos/" },
                    StringSplitOptions.RemoveEmptyEntries)[1]).Groups[1].Value, out id))
                {
                    //получили ID(!!!)
                    return id.ToString();
                }
            }
            //NOTE: "message": "An access token is required to request this resource.", 
            //NOTE: Увы можно считывать только свои ноты, так что беда
            //else if ((link.Contains("/notes/") || link.Contains("/note.php")) && !String.IsNullOrEmpty(AccessToken))
            //{
            //    Match m = Regex.Match(link.Split('/').Last(), @"(?:^|\b)(\d+)(?:\b|$)");
            //    if (m.Success && ulong.TryParse(m.Groups[1].Value, out id))
            //    {
            //        //SELECT uid, note_id, title, content_html, content, created_time, updated_time FROM note WHERE note_id="122788341354"
            //        var parameters = new Dictionary<string, object>();
            //        var quesries = new Dictionary<string, string>();
            //        quesries.Add("query1", String.Format(
            //            "SELECT uid, note_id, title, content_html, created_time, comment_info, like_info FROM note WHERE note_id='{0}'", id));
            //        quesries.Add("query2", "SELECT username FROM profile WHERE id in (SELECT uid FROM #query1)");
            //        parameters["queries"] = quesries;
            //        parameters["method"] = "fql.multiquery";
            //        dynamic data = FbClient.Get(parameters);
            //        var singleresult = data[0]["fql_result_set"][0];
            //        bp.Link = new Uri(String.Format("http://facebook.com/note.php?note_id={0}", singleresult["note_id"]));
            //        bp.Body = ((string)singleresult["title"] + " " + (string)singleresult["content_html"]).Trim();
            //        bp.SpecialParam = (string)singleresult["uid"];
            //        bp.Published = Common.ConvertFromUnixTimestamp(double.Parse((string)singleresult["created_time"]));
            //        if (!String.IsNullOrEmpty((string)data[1]["fql_result_set"][0]["username"]))
            //        {
            //            bp.SpecialParam = (string)data[1]["fql_result_set"][0]["username"];
            //        }
            //        //comments & likes
            //        if (((JsonObject)singleresult).ContainsKey("comment_info"))
            //        {
            //            bp.Comments = int.Parse(singleresult["comment_info"]["comment_count"]);
            //        }
            //        if (((JsonObject)singleresult).ContainsKey("like_info"))
            //        {
            //            //будем считать, что число лайков прямопропорционально просомтрам
            //            bp.Views = int.Parse(singleresult["like_info"]["like_count"]);
            //        }
            //    }
            //}
            return null;
        }

        /// <summary>
        /// Получение статистики по одной страницы путем ручного парсинга
        /// </summary>
        /// <param name="userName">имя поль</param>
        /// <param name="withFeed">запрашивать ли параметры фида?</param>
        /// <returns></returns>
        public FaceStats GetPageStats(string userName, bool withFeed = false)
        {
            var ps = new FaceStats();
            try
            {
                var parameters = new Dictionary<string, object>();
                //likes renamed to fan_count
                List<string> innerParams = new List<string> { "id", "about", "username", "name", "category", "talking_about_count", "fan_count", "is_verified", "description" };
                if (withFeed)
                {
                    innerParams.Add(FeedParams);
                }
                parameters["fields"] = innerParams.ToArray();

                dynamic data = FbClient.Get("/v2.10/" + userName, parameters);
                ps.Likes = data.fan_count == null ? 0 : data["fan_count"];
                ps.Talks = data.talking_about_count == null ? 0 : data["talking_about_count"];
                ps.Name = data["name"];
                if (data.about != null)
                {
                    ps.About = data["about"];
                }
                else if (data.description != null)
                {
                    ps.About = data["description"];
                }
                else
                {
                    ps.About = String.Empty;
                }
                if (data.category != null)
                {
                    ps.Subtitle = data["category"];
                }
                if (data.feed != null && data["feed"].data != null)
                {
                    ps.Feed = new List<BlogPost>();
                    foreach (var item in data["feed"].data)
                    {
                        var post = ParseJSON(item);
                        if (post != null)
                        {
                            ps.Feed.Add(post);
                        }
                    }
                }
                if (data.username != null)
                {
                    ps.Username = data["username"].ToString().ToLower();
                }
                ps.ID = data["id"];
                ps.IsVerified = data["is_verified"];
            }
            catch (FacebookOAuthException oauth)
            {
                if (oauth.ErrorCode == 1 && withFeed)//An unknown error has occurred.
                {
                    ps = GetPageStats(userName, false);
                }
                else if (oauth.ErrorCode == 2)
                {
                    //An unexpected error has occurred. Please retry your request later.
                    ps.IsServerError = true;
                }else if(oauth.ErrorCode == 21)
                {
                    //(OAuthException - #21) Page ID 1027157420661271 was migrated to page ID 1595405540780316.  Please update your API calls to the new ID
                    return ps;
                }
                else if (oauth.ErrorCode != 803)//попали в простого пользователя
                {
                    //Категории нет у простых пользователей, поэтому получается ошибка #100
                    if (!oauth.Message.Contains("(category)"))
                    {
                        ps.IsAuthError = true;
                    }
                }
                Debug.Write(oauth);
            }
            catch (Exception e1)
            {
                Debug.WriteLine("Ошибка загрузки статистики @pageName=" + userName + " " + e1.Message);
            }
            return ps;
        }

        /// <summary>
        /// Получение статистики для одного пользователя по номеру
        /// </summary>
        /// <param name="userName">имя пользователя</param>
        /// <returns></returns>
        public string GetLocale(string userName)
        {
            try
            {
                var parameters = new Dictionary<string, object>();
                parameters["fields"] = new[] { "name", "locale" };
                    dynamic data = FbClient.Get("/" + userName, parameters);
                    return (string) data["locale"];
            }
            catch (Exception e1)
            {
                Debug.WriteLine("Ошибка загрузки статистики: " + e1.Message);
            }
            return String.Empty;
        }

        /// <summary>
        /// Получение аватарки по имени
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public override string ExtractAvatar(string userName, BlogType bt)
        {
            string sRet = String.Empty;
            try
            {
                sRet = String.Format("https://graph.facebook.com/{0}/picture?type=square", userName);
                //var parameters = new Dictionary<string, object>();
                //parameters["fields"] = new []{"picture"};
                //dynamic data = FbClient.Get("/" + userName,parameters);
                //sRet = data["picture"]["data"]["url"];
            }
            catch (Exception e1)
            {
                Debug.WriteLine(e1.Message);
            }
            return sRet;
        }

        /// <summary>
        /// Получение ссылки на RSS для СТРАНИЦ Facebook'a
        /// </summary>
        /// <param name="userName">имя</param>
        /// <param name="btype">тип</param>
        /// <returns></returns>
        public override string GetRssLink(string userName, BlogType btype)
        {
            return String.Empty;
        }

        /// <summary>
        /// Получаем фид последних сообщений
        /// </summary>
        /// <param name="userName">идентификатор страницы</param>
        /// <returns></returns>
        /// <remarks>Требует ключа доступа access_token(!)</remarks>
        public List<BlogPost> GetRss(string userName)
        {
            List<BlogPost> oRet = new List<BlogPost>();
            try
            {
                var parameters = new Dictionary<string, object>();
                parameters["fields"] = new[] { FeedParams };
                dynamic data = FbClient.Get("/" + userName, parameters);
                if (data.feed != null && data["feed"].data != null)
                {
                    foreach (var item in data["feed"].data)
                    {
                        var post = ParseJSON(item);
                        if (post != null)
                        {
                            oRet.Add(post);
                        }
                    }
                }
            }
            catch (Exception e2)
            {
                Debug.Write(e2);
            }
            return oRet;
        }

        /// <summary>
        /// Разборка данных
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private static BlogPost ParseJSON(IDictionary<string, object> data)
        {
            var status = data["type"] as string;
            BlogPost bp = BlogPost.SimpleLink("http://facebook.com/" + data["id"]);
            Uri link;
            if (data.ContainsKey("link") && Uri.TryCreate(data["link"].ToString(), UriKind.Absolute, out link))
            {
                bp.Link = link;
            }
            bp.Links = 0;
            bp.Comments = 0;
            if (status == "link" || status == "video") //ссылка расшарена
            {
                bp.Link = new Uri("http://facebook.com/" + data["id"]);
            }

            if (!data.ContainsKey("from")) return null;
            var from = data["from"].ToString();
            if (String.IsNullOrEmpty(from)) return null;

            if (!DateTime.TryParse(data["created_time"] as string,
                CultureInfo.InvariantCulture,
                DateTimeStyles.AssumeUniversal, out bp.Published))
            {
                bp.Published = DateTime.UtcNow;//не смогли понять дату - считаем, что прямо сейчас
            }

            var jToken = JObject.Parse(from);
            if (jToken["name"] == null ||
                (!data.ContainsKey("message") &&
                 !data.ContainsKey("description") &&
                 !data.ContainsKey("story")
                ))
            {
                return null;
            }
            bp.Subject = (jToken["name"].Value<string>() + ": " +
                          (data.ContainsKey("message")
                               ? data["message"]
                               : (data.ContainsKey("story")
                                      ? data["story"]
                                      : data["description"]))).Trim();
            if (bp.Subject.Length > 250) bp.Subject = bp.Subject.Substring(0, 250);

            bp.SpecialParam = jToken["id"].Value<string>();
            if (data.ContainsKey("picture"))
            {
                bp.Attributes.Add(new PostAttribute(PostParams.MediaThumbnail, data["picture"].ToString()));
            }
            if (data.ContainsKey("caption"))
            {
                bp.Body = data["caption"].ToString();
            }

            #region TAGS

            string tags = String.Empty;
            if (data.ContainsKey("with_tags"))
            {
                var tt = data["with_tags"] as IDictionary<String, object>;
                if (tt != null)
                {
                    dynamic innertags = tt.FirstOrDefault().Value;
                    if (innertags != null)
                    {
                        foreach (var tagdata in innertags)
                        {
                            tags += tagdata["name"] + ",";
                        }
                    }
                }
            }

            if (data.ContainsKey("message_tags"))
            {
                var tt = data["message_tags"] as IDictionary<String, object>;
                if (tt != null)
                {
                    dynamic innertags = tt.FirstOrDefault().Value;
                    if (innertags != null)
                    {
                        foreach (var tagdata in innertags)
                        {
                            tags += tagdata["name"] + ",";
                        }
                    }
                }
            }

            #endregion

            #region Location

            if (data.ContainsKey("place"))
            {
                var place = data["place"] as IDictionary<String, object>;
                if (place != null)
                {
                    if (place.ContainsKey("name"))
                    {
                        tags += place["name"] + ",";
                    }
                    if (place.ContainsKey("location"))
                    {
                        var location = place["location"] as IDictionary<String, object>;
                        if (location != null)
                        {
                            bp.Attributes.Add(new PostAttribute(PostParams.Location,
                                                                (location["latitude"] + " " +
                                                                location["longitude"])));
                        }
                    }
                }
            }
            
            #endregion

            if (data.ContainsKey("shares"))
            {
                var shares = data["shares"] as IDictionary<String, object>;
                if (shares != null)
                {
                    bp.Links += int.Parse(shares["count"].ToString());
                }
            }

            if (data.ContainsKey("reactions"))
            {
                var reactions = data["reactions"] as IDictionary<String, object>;
                if (reactions != null && reactions.ContainsKey("summary"))
                {
                    var summary = reactions["summary"] as IDictionary<String, object>;
                    if (summary != null && summary.ContainsKey("total_count"))
                    {
                        bp.Links += int.Parse(summary["total_count"].ToString());
                    }
                }
            }

            if (data.ContainsKey("comments"))
            {
                var comments = data["comments"] as IDictionary<String, object>;
                if (comments != null)
                {
                    foreach (JToken comment in JArray.Parse(comments["data"].ToString()))
                    {
                        bp.Views += comment["like_count"].Value<int>();
                        bp.Comments++;
                    }
                    bp.Views += bp.Comments;
                }
            }
            bp.Views += bp.Links;

            if (!String.IsNullOrEmpty(tags))
            {
                tags = tags.TrimEnd(',');
                bp.Attributes.Add(new PostAttribute(PostParams.Tags, tags));
            }
            return bp;
        }

        /// <summary>
        /// Получение несколько постов по ссылкам, групповая операция
        /// </summary>
        public List<BlogPost> GetManyPost(params string[] links)
        {
            var bpRet = new List<BlogPost>();
            foreach (string link in links)
            {
                Debug.WriteLine("Facebook.GetManyPost:" + link);
                BlogPost bp = BlogPost.SimpleLink(link);
                try
                {
                    bp.Comments = 0;
                    bp.Links = 0;
                    var postId = TryGetPostIdAdvanced(link);
                    if (!String.IsNullOrEmpty(postId))
                    {
                        dynamic data = FbClient.Get("/v2.3/" + postId);
                        ProcessFacebookData(data, ref bp);
                    }
                }
                catch (Exception e1)
                {
                    Debug.Write(e1);
                }finally
                {
                    if (!String.IsNullOrEmpty(bp.Body))
                    {
                        bp.Existance = true;
                        bpRet.Add(bp);
                    }
                }
                Debug.WriteLine("result:" + bp.Existance+" , "+bp.Subject);
            }
            return bpRet;
        }

        /// <summary>
        /// Преобразование данных с ФБ в пост
        /// </summary>
        /// <param name="data"></param>
        /// <param name="bp"></param>
        private void ProcessFacebookData(dynamic data, ref BlogPost bp)
        {
            try
            {
                bp.Body = data.name != null ? data["name"]+" " : "";
                if (data.message != null)
                {
                    bp.Body += data["message"]+" ";
                }
                if (data.description != null)
                {
                    bp.Body += data["description"]+" ";
                }
                bp.Body = bp.Body.Trim();
                bp.Subject = bp.Body;
                if (bp.Subject.Length > 100)
                {
                    bp.Subject = bp.Subject.Substring(0, 100);
                }
                if (data.from != null && data["from"].id != null)
                {
                    bp.SpecialParam = data["from"]["id"].Split('_')[0]; //идентификатор пользователя
                }
                if (data.link != null)
                {
                    if (!Uri.TryCreate(data["link"], UriKind.Absolute, out bp.Link))
                    {
                        Uri.TryCreate("https://facebook.com/" + data["link"].TrimStart('/'), UriKind.Absolute,
                            out bp.Link);
                    }
                }
                if (data.picture != null)
                {
                    bp.Attributes.Add(new PostAttribute(PostParams.MediaThumbnail, data["picture"]));
                }
                if (data.likes != null)
                {
                    bp.Views = data["likes"]["data"].Count;
                }
                if (data.comments != null)
                {
                    bp.Comments = data["comments"]["data"].Count;
                    foreach (dynamic comment in data["comments"]["data"])
                    {
                        if (comment.like_count != null)
                        {
                            bp.Views += comment["like_count"];
                        }
                    }
                }
                if (data.place != null)
                {
                    if (data["place"].name != null)
                    {
                        bp.Attributes.Add(new PostAttribute(PostParams.Tags, data["place"]["name"]));
                    }
                    if (data["place"].location != null &&
                        data["place"]["location"].latitude != null &&
                        data["place"]["location"].longitude != null)
                    {
                        bp.Attributes.Add(new PostAttribute(PostParams.Location,
                             String.Format(new CultureInfo("en-US"), "{0} {1}",
                            data["place"]["location"]["latitude"], data["place"]["location"]["longitude"])
                            ));
                    }
                }
                if (data.location != null)
                {
                    bp.Attributes.Add(new PostAttribute(PostParams.Tags, data["location"]));
                }
                if (data.venue != null)
                {
                    if (data["venue"].latitude != null &&
                        data["venue"].longitude != null)
                    {
                        bp.Attributes.Add(new PostAttribute(PostParams.Location,
                             String.Format(new CultureInfo("en-US"), "{0} {1}",
                            data["venue"]["latitude"], data["venue"]["longitude"])));
                    }
                }
                bp.Published = DateTime.Parse(data.updated_time != null ? data["updated_time"] : data["created_time"]);
                if (bp.Published > DateTime.UtcNow)
                {
                    bp.Published = DateTime.UtcNow;
                }
            }
            catch (RuntimeBinderException)
            {
                //  MyProperty doesn't exist
            }
        }

        /// <summary>
        /// Получение одного поста по ссылке на него и данных о пользователе
        /// </summary>
        /// <param name="linkOnPost">ссылка на пост</param>
        public override BlogPost GetOnePost(string linkOnPost)
        {
            return GetOnePost(linkOnPost, "", BlogType.Unknown);
        }

        /// <summary>
        /// Получение одного поста по ссылке на него и данных о пользователе
        /// </summary>
        /// <param name="linkOnPost">ссылка на пост</param>
        /// <param name="userName">имя пользователя</param>
        /// <param name="btype">тип блогплатформы</param>
        /// <remarks>Может генерить исключения</remarks>
        public override BlogPost GetOnePost(string linkOnPost, string userName, BlogType btype)
        {
            List<BlogPost> bps = GetManyPost(new[] { linkOnPost });
            return bps.Count > 0 ? bps[0] : null;
        }

        #endregion

        /// <summary>
        /// Получение списка прокомментировавших и пролайкавших
        /// </summary>
        /// <param name="postId">идентификатор поста</param>
        /// <param name="comments">массив комментариев</param>
        /// <example>260603653970023_1371362582894119/comments?fields=from&limit=100</example>
        /// <returns></returns>
        public Dictionary<string, string> GetUsersAndCommentsByPost(string postID, List<CommentPost> comments = null)
        {
            Dictionary<string, string> allIds = new Dictionary<string, string>();
            try
            {
                var paging = "";
                var finished = true;
                //comments
                do
                {
                    dynamic data = FbClient.Get(String.Format("{0}/comments?pretty=0{1}&limit=500{2}", postID , 
                        comments==null?"&fields=from":"", //экономиим тело ответа
                        paging));
                    foreach (var comment in data["data"])
                    {
                        if (comment.from != null && comment["from"].id != null)
                        {
                            if (!allIds.ContainsKey(comment["from"].id))
                            {
                                allIds.Add(comment["from"].id, comment["from"].name);
                            }
                            if(comments != null)//
                            {
                                comments.Add(new CommentPost()
                                {
                                    Link = new Uri("https://facebook.com/" + comment["id"]),
                                    Subject = comment["from"]["name"] + ": " + comment["message"],
                                    Existance = true,
                                    Published = DateTime.Parse(comment["created_time"]),
                                    SpecialParam = comment["from"]["id"].ToString()//кто написал, пойдет в blogname
                                });
                            }
                        }
                    }
                    if (data.paging != null && data["paging"].next != null)
                    {
                        paging = "&after=" + data["paging"]["cursors"]["after"].ToString();
                        finished = false;
                    }
                    else
                    {
                        finished = true;
                    }
                } while (!finished);
                //likes
                paging = "";
                do
                {
                    dynamic data = FbClient.Get(postID + "/likes?pretty=0&fields=id,name&limit=500" + paging);
                    foreach (var likes in data["data"])
                    {
                        if (likes.id != null && likes.name != null && !allIds.ContainsKey(likes.id))
                        {
                            allIds.Add(likes.id, likes.name);
                        }
                    }
                    if (data.paging != null && data["paging"].next != null)
                    {
                        paging = "&after=" + data["paging"]["cursors"]["after"].ToString();
                        finished = false;
                    }
                    else
                    {
                        finished = true;
                    }
                } while (!finished);

            }
            catch(Exception e1)
            {
                Debug.Write(e1);
            }
            return allIds;
        }

        /// <summary>
        /// Поиск страниц в фейсбуке
        /// </summary>
        /// <param name="query">поисковый запрос</param>
        /// <param name="max">ограничение на колчество</param>
        /// <returns></returns>
        [Obsolete]
        public List<String> SearchForPages(string query, int max=10)
        {
            var oRet = new List<string>(0);
            string searchurl =
                "http://www.facebook.com/ajax/search/blendedsearch/more.php?q={0}&type=pages&init=srp&s={1}&__a=1";
            try
            {
                for (int i=0;  oRet.Count<max ; i+=10)//шаг в 10 результатов
                {
                    myHttpWebRequest = MakeRequest(String.Format(searchurl,HttpUtility.UrlEncode(query),i));
                    myHttpWebRequest.Method = "GET"; // метод GET
                    myHttpWebRequest.UserAgent =
                        "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1";
                    myHttpWebRequest.Accept =
                        "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
                    myHttpWebRequest.Headers.Add("Accept-Language", "en-us,en;q=0.5");
                    myHttpWebRequest.Headers.Add("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
                    //получаем ответ и его разбираем...
                    myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
                    string result = new StreamReader(myHttpWebResponse.GetResponseStream(), Encoding.UTF8).ReadToEnd();
                    myHttpWebResponse.Close();
                    MatchCollection mc = rParser.Matches(result);
                    if (mc.Count == 0) break;
                    foreach (Match match in mc)
                    {
                        if(String.Compare(match.Groups[1].Value,"pages")==0)
                        {
                            oRet.Add(match.Groups[2].Value);
                        }else
                        {
                            oRet.Add(match.Groups[1].Value.ToLower());
                        }
                    }
                }
            }catch(Exception e1)
            {
                Debug.WriteLine(e1.Message);
            }
            return oRet;
        }

        /// <summary>
        /// Попытка получения списка страниц, которые были залайканы текущей страницей или пользователем
        /// </summary>
        /// <returns></returns>
        [Obsolete]
        public List<String> GetFanedRussianPages(UserAccount user, string pageName)
        {
            var oRet = new List<string>();
            if (LoggedInAs == null && !LogInto(user))
            {
                //не удалось залогинится
                return oRet;
            }
            try
            {
                string pageID = String.Empty;
                if(Regex.IsMatch(pageName,@"^\D$",Regexoptions))
                {
                    pageID = pageName;
                }else//ищем номер страницы по имени
                {
                    pageID = GetPageIdByName(pageName);
                    if(String.IsNullOrEmpty(pageID))
                    {
                        return oRet;
                    }
                }
                myHttpWebRequest = MakeRequest(String.Format("http://www.facebook.com/browse/fanned_pages/?id={0}",pageID));
                myHttpWebRequest.Method = "GET"; // метод GET
                myHttpWebRequest.Referer = "http://facebook.com";
                //myHttpWebRequest.AllowAutoRedirect = false;//на случай если страница переместилась
                //получаем ответ и его разбираем...
                myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
                string result = new StreamReader(myHttpWebResponse.GetResponseStream(), Encoding.UTF8).ReadToEnd();
                myHttpWebResponse.Close();
                Facebook fb = new Facebook();
                MatchCollection mc = Regex.Matches(result, @"https?://(?:[-_\w]+\.)?facebook\.com/(?:([0-9a-z\._-]+)|[^/]+/[^/]+/(\d+)(?:[^0-9]*?))[^/>]+&quot;(\d+)\D", Regexoptions);
                foreach (Match m in mc)
                {
                    string userName = m.Groups[1].Success ? m.Groups[1].Value.ToLower() : m.Groups[2].Value;
                    string userID = m.Groups[3].Value;
                    //проверяем русскоязычность =(
                    if (!String.IsNullOrEmpty(userName) && !oRet.Contains(userName) && userName != "0")
                    {
                        if (!Common.IsRussian(m.Groups[0].Value))//в случае номера русским может быть само имя
                        {
                            //нерусское, поэтому смотрим на RSS
                            Boolean? isrus = fb.IsRussian(userID);
                            if (isrus.HasValue && isrus.Value)
                            {
                                oRet.Add(userName);
                            }
                        }
                        else
                        {//русское, добавляемся
                            oRet.Add(userName);
                        }
                    }
                }
            }
            catch (Exception e1)
            {
                Debug.WriteLine(e1.Message);
            }
            return oRet;
        }

        /// <summary>
        /// Получение номера страницы по имени
        /// </summary>
        /// <remarks>Может не работать</remarks>
        /// <param name="userName"></param>
        /// <returns></returns>
        public string GetPageIdByName(string userName)
        {
            string sRet = String.Empty;
            try
            {
                dynamic data = FbClient.Get("/" + userName);
                sRet = data["id"];
            }
            catch (Exception e1)
            {
                Debug.WriteLine(e1.Message);
            }
            return sRet;
        }

        #region ILike

        /// <summary>
        /// Получение статистики лайков и шаров для ссылки
        /// </summary>
        /// <example>?fields=share&id=http%3A%2F%2Fyoutube.com%2Fwatch%3Fv%3DLg2WjLysdI4</example>
        /// <param name="Link">ссылка на страницу, для которой надо определить шары и комменты</param>
        /// <remarks>Version 2.0 of the Facebook Platform API is the last version where FQL will be available. Versions after 2.0 will not support FQL. Please migrate your applications to use Graph API instead of FQL. Please see our changelog for current version information.</remarks>
        /// <returns></returns>
        public Dictionary<string, int> GetLikes(Uri Link)
        {
            var oRet = new Dictionary<string, int>();
            try
            {
                dynamic data =
                    FbClient.Get(String.Format("/v2.5/?fields=share&id={0}", HttpUtility.UrlEncode(Link.OriginalString)));
                if (data.share != null)
                {
                    if (data["share"].comment_count != null)
                        oRet.Add("comments", int.Parse(data["share"]["comment_count"].ToString()));
                    if (data["share"].like_count != null)
                        oRet.Add("likes", int.Parse(data["share"]["like_count"].ToString()));
                    if (data["share"].share_count != null)
                        oRet.Add("shares", int.Parse(data["share"]["share_count"].ToString()));
                    //чтобы было что-то от фейсбука
                    if (oRet.Count == 0)
                    {
                        oRet.Add("shares", 0);
                    }
                }
            }
            catch (FacebookOAuthException e0)
            {
                Debug.WriteLine(e0);//токен истек(!)
            }
            catch (Exception e1)//FacebookApiLimitException
            {
                Debug.WriteLine(e1);
            }
            return oRet;
        }

        #endregion
    }
}
